import cv2
import numpy as np
import pandas as pd
import os


class CellImageProcessor:
    def __init__(self, excel_path):
        """
        初始化细胞图像处理器
        :param excel_path: Excel文件路径
        """
        self.excel_path = excel_path
        self.label_df = None
        self.cell_counts = {0: 0, 1: 0, 2: 0}
        self.colors = {
            0: (0, 255, 0),  # 绿色
            1: (0, 0, 255),  # 红色
            2: (0, 165, 255)  # 橙色
        }
        self.load_csv_data()

    def load_csv_data(self):
        """加载CSV数据"""
        try:
            self.label_df = pd.read_csv(self.excel_path)
            if 'filename' not in self.label_df.columns or 'Predicted_Label' not in self.label_df.columns:
                raise ValueError("Excel文件必须包含 'filename' 和 'Predicted_Label' 列")
        except Exception as e:
            print(f"读取Excel文件时出错: {str(e)}")
            raise

    def load_image(self, input_url):
        """
        加载图像
        :param input_url: 图像URL或本地路径
        :return: 加载的图像
        """
        try:    
            print(f"加载图像: {input_url}")
            # 本地路径
            image = cv2.imread(input_url)
            if image is None:
                raise ValueError(f"无法读取图像: {input_url}")
            return image
        except Exception as e:
            print(f"加载图像时出错: {str(e)}")
            raise

    def preprocess_image(self, image):
        """
        图像预处理
        :param image: 输入图像
        :return: 预处理后的二值图像
        """
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        gray = cv2.bitwise_not(gray)
        _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
        kernel = np.ones((5, 5), np.uint8)
        return cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel, iterations=2)

    def is_valid_cell(self, area, circularity, aspect_ratio, min_area, max_area):
        """
        判断是否为有效的细胞
        :return: 布尔值
        """
        return (min_area < area < max_area and
                circularity > 0.5 and
                0.6 < aspect_ratio < 1.4)

    def draw_cell(self, image, contour, center, cell_count, color):
        """
        在图像上绘制细胞
        :param image: 目标图像
        :param contour: 轮廓
        :param center: 中心点
        :param cell_count: 细胞计数
        :param color: 轮廓颜色
        """
        cx, cy = center
        cv2.drawContours(image, [contour], -1, color, 2)
        cv2.circle(image, (cx, cy), 2, (0, 0, 255), -1)
        cv2.putText(image, str(cell_count), (cx - 10, cy - 10),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)

    def process_image(self, patientId, recordId, input_url):
        """
        处理图像的主方法
        :param input_url: 输入图像的URL或路径
        :param patientId: 患者ID
        :param recordId: 记录ID
        :return: 处理结果和细胞计数
        """
        try:
            # 加载和预处理图像
            image = self.load_image(input_url)
            binary = self.preprocess_image(image)

            # 创建结果图像
            result_images = {
                label: image.copy() for label in self.cell_counts.keys()
            }

            # 连通组件分析
            num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary, connectivity=8)

            # 计算面积范围
            areas = stats[1:, cv2.CC_STAT_AREA]
            if len(areas) == 0:
                raise ValueError("未检测到任何细胞")
            median_area = np.median(areas)
            min_area = median_area * 0.3
            max_area = median_area * 2.0

            print(f"中位数面积: {median_area}")
            print(f"面积范围: {min_area} - {max_area}")

            # 重置计数器
            self.cell_counts = {label: 0 for label in self.cell_counts.keys()}
            cell_count = 0

            # 处理每个标签
            for label in range(1, num_labels):
                stats_current = stats[label]
                area = stats_current[cv2.CC_STAT_AREA]
                x, y, w, h = stats_current[cv2.CC_STAT_LEFT:cv2.CC_STAT_LEFT + 4]

                aspect_ratio = float(w) / h if h > 0 else 0
                component_mask = (labels == label).astype(np.uint8) * 255
                contours, _ = cv2.findContours(component_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

                if contours:
                    contour = contours[0]
                    perimeter = cv2.arcLength(contour, True)
                    circularity = 4 * np.pi * area / (perimeter * perimeter) if perimeter > 0 else 0

                    if self.is_valid_cell(area, circularity, aspect_ratio, min_area, max_area):
                        # 获取预测标签
                        cell_label = self.label_df.iloc[cell_count]['Predicted_Label'] if cell_count < len(
                            self.label_df) else 2
                        center = tuple(map(int, centroids[label]))

                        # 绘制细胞
                        self.draw_cell(result_images[cell_label], contour, center,
                                       self.cell_counts[cell_label], self.colors[cell_label])
                        self.cell_counts[cell_label] += 1
                        cell_count += 1

            # 保存结果
            output_dir = os.path.join("temp", patientId, recordId)
            os.makedirs(output_dir, exist_ok=True)

            result_paths = []
            # 设置保存图片的质量参数，1到100的整数，越小图片越小，质量越差
            quality = 25  # 可以根据需要调整这个值
            for label, image in result_images.items():
                output_file = os.path.join(output_dir, f"result_label_{label}.jpg")
                # 获取绝对路径
                output_file_absolute = os.path.abspath(output_file)
                # 使用imwrite的params参数控制图片质量
                cv2.imwrite(output_file_absolute, image, [cv2.IMWRITE_JPEG_QUALITY, quality])
                result_paths.append(output_file_absolute)

            # 打印统计信息
            print(f"标签0（绿色）细胞数量: {self.cell_counts[0]}")
            print(f"标签1（红色）细胞数量: {self.cell_counts[1]}")
            print(f"标签2（橙色）细胞数量: {self.cell_counts[2]}")
            print(f"总检测细胞数量: {cell_count}")

            # 计算百分比
            total = cell_count
            normal = self.cell_counts[0]
            棘红细胞 = self.cell_counts[1]
            椭圆形红细胞 = self.cell_counts[2]

            result = {
                "total": total,
                "recordId": recordId,
                "details": [
                    {
                        "type": "正常细胞",
                        "quantity": normal,
                        "percentage": f"{normal / total:.5f}" if total > 0 else "0.00000",
                        "imgPath": result_paths[0]
                    },
                    {
                        "type": "棘红细胞",
                        "quantity": 棘红细胞,
                        "percentage": f"{棘红细胞 / total:.5f}" if total > 0 else "0.00000",
                        "imgPath": result_paths[1]
                    },
                    {
                        "type": "椭圆形红细胞",
                        "quantity": 椭圆形红细胞,
                        "percentage": f"{椭圆形红细胞 / total:.5f}" if total > 0 else "0.00000",
                        "imgPath": result_paths[2]
                    }
                ]
            }

            return result

        except Exception as e:
            print(f"处理图像时出错: {str(e)}")
            return None

    def main(self, patientId, recordId, image_url):
        try:
            # 处理图像
            result = self.process_image(patientId, recordId, image_url)

            if result is not None:
                print("成功处理图像并保存三个结果文件：")
                print(result)
                return result       
            else:
                print("处理图像失败")

        except Exception as e:
            print(f"程序执行出错: {str(e)}")
